////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef __WN_BENAPHORE_INL__
#define __WN_BENAPHORE_INL__

#include "WNCore/inc/WNBase.h"
#include "WNConcurrency/inc/WNInterlocked.h"

__WN_INLINE WN_VOID WNBenaphoreInitialize(WN_BENAPHORE_T* _benaphore) {
    WN_RELEASE_ASSERT(_benaphore != WN_NULL);

    _benaphore->mCounter = 0;

    WNSemaphoreInitialize(&(_benaphore->mSemaphore));
}

__WN_INLINE WN_VOID WNBenaphoreDestroy(WN_BENAPHORE_T* _benaphore) {
    WN_RELEASE_ASSERT(_benaphore != WN_NULL);

    WNSemaphoreDestroy(&(_benaphore->mSemaphore));
}

__WN_INLINE WN_VOID WNBenaphoreLock(WN_BENAPHORE_T* _benaphore) {
    WN_RELEASE_ASSERT(_benaphore != WN_NULL);

    if (WNInterlockedIncrement(&(_benaphore->mCounter)) > 1) {
        WNSemaphoreWait(&(_benaphore->mSemaphore));
    }
}

__WN_INLINE WN_BOOL WNBenaphoreTryLock(WN_BENAPHORE_T* _benaphore) {
    WN_RELEASE_ASSERT(_benaphore != WN_NULL);

    return(WNInterlockedCompareSwap(&(_benaphore->mCounter), 1, 0) != 0 ? WN_TRUE : WN_FALSE);
}

__WN_INLINE WN_VOID WNBenaphoreUnlock(WN_BENAPHORE_T* _benaphore) {
    WN_RELEASE_ASSERT(_benaphore != WN_NULL);

    if (WNInterlockedDecrement(&(_benaphore->mCounter)) > 0) {
        WNSemaphorePost(&(_benaphore->mSemaphore));
    }
}

#ifdef _WN_CPLUSPLUS
    __WN_INLINE WNBenaphore::WNBenaphore() {
        WNBenaphoreInitialize(&mBenaphore);
    }

    __WN_INLINE WNBenaphore::~WNBenaphore() {
        WNBenaphoreDestroy(&mBenaphore);
    }

    __WN_INLINE WN_VOID WNBenaphore::Lock() {
        WNBenaphoreLock(&mBenaphore);
    }

    __WN_INLINE WN_BOOL WNBenaphore::TryLock() {
        WNBenaphoreTryLock(&mBenaphore);
    }

    __WN_INLINE WN_VOID WNBenaphore::Unlock() {
        WNBenaphoreUnlock(&mBenaphore);
    }

    __WN_INLINE WNBenaphoreScopeLock::WNBenaphoreScopeLock(WN_BENAPHORE_T* _benaphore) {
        WN_RELEASE_ASSERT(_benaphore != WN_NULL);

        mBenaphore = _benaphore;

        WNBenaphoreLock(mBenaphore);
    }

    __WN_INLINE WNBenaphoreScopeLock::WNBenaphoreScopeLock(WNBenaphore& _benaphore) {
        mBenaphore = &(_benaphore.mBenaphore);

        WNBenaphoreLock(mBenaphore);
    }

    __WN_INLINE WNBenaphoreScopeLock::~WNBenaphoreScopeLock() {
        WNBenaphoreUnlock(mBenaphore);
    }
#endif

#endif // __WN_BENAPHORE_INL__